////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2001.
// -------------------------------------------------------------------------
//  File name:   animnode.cpp
//  Version:     v1.00
//  Created:     23/4/2002 by Timur.
//  Compilers:   Visual C++ 7.0
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "AnimNode.h"
#include "AnimTrack.h"
#include "AnimSequence.h"
#include "CharacterTrack.h"
#include "AnimSplineTrack.h"
#include "BoolTrack.h"
#include "SelectTrack.h"
#include "EventTrack.h"
#include "SoundTrack.h"
#include "ExprTrack.h"
#include "ConsoleTrack.h"
#include "MusicTrack.h"
#include "FaceSeqTrack.h"
#include "LookAtTrack.h"
#include "TrackEventTrack.h"
#include "SequenceTrack.h"
#include "CompoundSplineTrack.h"
#include "GotoTrack.h"
#include "CaptureTrack.h"
#include "CommentTrack.h"
#include "ScreenFaderTrack.h"
#include <I3DEngine.h>
#include <ctime>

//////////////////////////////////////////////////////////////////////////
// Old depricated IDs
//////////////////////////////////////////////////////////////////////////
#define APARAM_CHARACTER4 (APARAM_USER + 0x10)
#define APARAM_CHARACTER5 (APARAM_USER + 0x11)
#define APARAM_CHARACTER6 (APARAM_USER + 0x12)
#define APARAM_CHARACTER7 (APARAM_USER + 0x13)
#define APARAM_CHARACTER8 (APARAM_USER + 0x14)
#define APARAM_CHARACTER9 (APARAM_USER + 0x15)
#define APARAM_CHARACTER10 (APARAM_USER + 0x16)

#define APARAM_EXPRESSION4 (APARAM_USER + 0x20)
#define APARAM_EXPRESSION5 (APARAM_USER + 0x21)
#define APARAM_EXPRESSION6 (APARAM_USER + 0x22)
#define APARAM_EXPRESSION7 (APARAM_USER + 0x23)
#define APARAM_EXPRESSION8 (APARAM_USER + 0x24)
#define APARAM_EXPRESSION9 (APARAM_USER + 0x25)
#define APARAM_EXPRESSION10 (APARAM_USER + 0x26)
//////////////////////////////////////////////////////////////////////////

static const int DEFAULT_TRACK_TYPE = -1;

//////////////////////////////////////////////////////////////////////////
// CAnimNode.
//////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////
void CAnimNode::Activate( bool bActivate )
{
}

//////////////////////////////////////////////////////////////////////////
int CAnimNode::GetTrackCount() const
{
	return m_tracks.size();
}

const char* CAnimNode::GetParamName( AnimParamType param ) const
{
	switch ((int)param)
	{
	case APARAM_FOV:
		return "FOV";
	case APARAM_POS:
		return "Position";
	case APARAM_ROT:
		return "Rotation";
	case APARAM_SCL:
		return "Scale";
	case APARAM_VISIBLE:
		return "Visibility";
	case APARAM_EVENT:
		return "Events";
	case APARAM_TRACKEVENT:
		return "Track Events";
	case APARAM_CAMERA:
		return "Camera";
	
	// Sound tracks.
	case APARAM_SOUND:
		return "Sound";

	// Character tracks.
	case APARAM_ANIMATION:
		return "Animation";

	case APARAM_EXPRESSION:
		return "Expression";

	case APARAM_SEQUENCE:
		return "Sequence";
	case APARAM_CONSOLE:
		return "Console";
	case APARAM_MUSIC:
		return "Music";

	case APARAM_FLOAT_1:
		return "Value";
	case APARAM_CAPTURE:
		return "Capture";
	case APARAM_TIMEWARP:
		return "Timewarp";
	case APARAM_FIXEDTIMESTEP:
		return "FixedTimeStep";
	}
	return "Unknown";
}

IAnimTrack* CAnimNode::GetTrackForParameter( int param ) const
{
	for (int i = 0,num = (int)m_tracks.size(); i < num; i++)
	{
		if (m_tracks[i].paramId == param)
			return m_tracks[i].track;

		// Search the subtracks also if any.
		for(int k = 0; k < m_tracks[i].track->GetSubTrackCount(); ++k)
		{
			if (m_tracks[i].track->GetSubTrack(k)->GetParameterType() == param)
				return m_tracks[i].track->GetSubTrack(k);
		}
	}
	return 0;
}

IAnimTrack* CAnimNode::GetTrackForParameter( int nParamId, uint32 index ) const
{
	SParamInfo paramInfo;
	bool ok = GetParamInfoFromId(nParamId, paramInfo);
	assert(ok);
	if ((paramInfo.flags & IAnimNode::PARAM_MULTIPLE_TRACKS) == 0)
		return GetTrackForParameter(nParamId);

	uint32 count = 0;
	for (int i = 0,num = (int)m_tracks.size(); i < num; i++)
	{
		if (m_tracks[i].paramId == nParamId
			&& count++ == index)
			return m_tracks[i].track;

		// For this case, no subtracks are considered.
	}
	return 0;
}

uint32 CAnimNode::GetTrackParamIndex( const IAnimTrack *pTrack ) const
{
	assert(pTrack);
	uint32 index = 0;
	AnimParamType paramType = pTrack->GetParameterType();
	
	SParamInfo paramInfo;
	bool ok = GetParamInfoFromId(paramType, paramInfo);
	assert(ok);
	if ((paramInfo.flags & IAnimNode::PARAM_MULTIPLE_TRACKS) == 0)
		return 0;

	for (int i = 0,num = (int)m_tracks.size(); i < num; i++)
	{
		if (m_tracks[i].track == pTrack)
			return index;

		if (m_tracks[i].paramId == paramType)
			++index;

		// For this case, no subtracks are considered.
	}
	assert(!"CAnimNode::GetTrackParamIndex() called with an invalid argument!");
	return 0;
}

IAnimTrack* CAnimNode::GetTrackByIndex( int nIndex ) const
{
	if (nIndex>=(int)m_tracks.size())
	{
		assert("nIndex>=m_tracks.size()"&&false);
		return NULL;
	}
	return m_tracks[nIndex].track;
}

void CAnimNode::SetTrack( int param,IAnimTrack *track )
{
	if (track)
	{
		for (unsigned int i = 0; i < m_tracks.size(); i++)
		{
			if (m_tracks[i].paramId == param)
			{
				m_tracks[i].track = track;
				return;
			}
		}

		AddTrack( param,track );
	}
	else
	{
		// Remove track at this id.
		for (unsigned int i = 0; i < m_tracks.size(); i++)
		{
			if (m_tracks[i].paramId == param)
			{
				m_tracks.erase( m_tracks.begin() + i );
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::TrackOrder(const CAnimNode::TrackDesc& left, const CAnimNode::TrackDesc& right)
{
	return left.paramId < right.paramId;
}

//////////////////////////////////////////////////////////////////////////
void CAnimNode::AddTrack( int param,IAnimTrack *track )
{
	TrackDesc td;
	td.paramId = param;
	td.track = track;
	track->SetParameterType( (AnimParamType)param);
	track->SetTimeRange(GetSequence()->GetTimeRange());
	m_tracks.push_back(td);



	std::stable_sort(m_tracks.begin(), m_tracks.end(), TrackOrder);

}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::RemoveTrack( IAnimTrack *pTrack )
{
	for (unsigned int i = 0; i < m_tracks.size(); i++)
	{
		if (m_tracks[i].track == pTrack)
		{
			m_tracks.erase( m_tracks.begin() + i );
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
IAnimTrack* CAnimNode::CreateTrackInternal( int paramId,int trackType )
{
	SParamInfo info;
	if (!GetParamInfoFromId(paramId,info))
	{
		return 0;
	}

	IAnimTrack *pTrack = NULL;
	switch (info.valueType)
	{
	case AVALUE_FLOAT:   
		pTrack = CreateTrackInternalFloat(trackType);
		break;
	
	case AVALUE_VECTOR:
		pTrack = CreateTrackInternalVector(trackType, info);
		break;

	case AVALUE_QUAT:
		pTrack = CreateTrackInternalQuat(trackType, info);
		break;
	case AVALUE_EVENT:   pTrack=new CEventTrack; break;
	case AVALUE_BOOL:    pTrack=new CBoolTrack; break;
	case AVALUE_SELECT:  pTrack=new CSelectTrack; break;
	case AVALUE_SOUND:   pTrack=new CSoundTrack; break;
	case AVALUE_CHARACTER: pTrack=new CCharacterTrack; break;
	case AVALUE_EXPRESSION:pTrack=new CExprTrack; break;
	case AVALUE_CONSOLE: pTrack=new CConsoleTrack; break;
	case AVALUE_MUSIC:   pTrack=new CMusicTrack; break;
	case AVALUE_FACESEQ: pTrack=new CFaceSeqTrack; break;
	case AVALUE_LOOKAT:  pTrack=new CLookAtTrack; break;
	case AVALUE_TRACKEVENT:   pTrack=new CTrackEventTrack; break;
	case AVALUE_SEQUENCE: pTrack=new CSequenceTrack; break;
	case AVALUE_DISCRETE_FLOAT:	pTrack = new CGototTrack; break;
	case AVALUE_CAPTURE:	pTrack = new CCaptureTrack; break;
	case AVALUE_COMMENT_TEXT: pTrack=new CCommentTrack; break;
	case AVALUE_SCREENFADER: pTrack = new CScreenFaderTrack; break;

	case AVALUE_VECTOR4:
		pTrack = CreateTrackInternalVector4(info);
		break;
	}

	if (pTrack)
		AddTrack( paramId,pTrack );
	return pTrack;
}

//////////////////////////////////////////////////////////////////////////
IAnimTrack* CAnimNode::CreateTrack( int paramId )
{
	return CreateTrackInternal( paramId,DEFAULT_TRACK_TYPE );
}

//////////////////////////////////////////////////////////////////////////
void CAnimNode::SerializeAnims( XmlNodeRef &xmlNode,bool bLoading, bool bLoadEmptyTracks )
{
	if (bLoading)
	{
		// Delete all tracks.
		stl::free_container(m_tracks);

		IAnimNode::SParamInfo info;
		// Loading.
		int paramId = -1;
		int num = xmlNode->getChildCount();
		for (int i = 0; i < num; i++)
		{
			XmlNodeRef trackNode = xmlNode->getChild(i);
			trackNode->getAttr( "ParamId",paramId );

			int nTrackType = DEFAULT_TRACK_TYPE;
			trackNode->getAttr( "Type",nTrackType );
			if (nTrackType == DEFAULT_TRACK_TYPE)
			{
				//////////////////////////////////////////////////////////////////////////
				// Backward compatibility code
				//////////////////////////////////////////////////////////////////////////
				// Legacy animation track.
				// Collapse parameter ID to the single type
				if (paramId >= APARAM_SOUND && paramId <= APARAM_SOUND+2)
					paramId = APARAM_SOUND;
				if (paramId >= APARAM_ANIMATION && paramId <= APARAM_ANIMATION+2)
					paramId = APARAM_ANIMATION;
				if (paramId >= APARAM_CHARACTER4 && paramId <= APARAM_CHARACTER10)
					paramId = APARAM_ANIMATION;
				if (paramId >= APARAM_EXPRESSION && paramId <= APARAM_EXPRESSION+2)
					paramId = APARAM_EXPRESSION;
				if (paramId >= APARAM_EXPRESSION4 && paramId <= APARAM_EXPRESSION10)
					paramId = APARAM_EXPRESSION;
				
				// Old tracks always used TCB tracks.
				// Backward compatibility to the CryEngine2 for track type (will make TCB controller)
				nTrackType = -2;
				//////////////////////////////////////////////////////////////////////////
			}
			
			IAnimTrack *track = CreateTrackInternal( paramId,nTrackType );
			if (track)
			{
				if (!track->Serialize( trackNode,bLoading,bLoadEmptyTracks ))
				{
					// Boolean tracks must always be loaded even if empty.
					if (track->GetType() != ATRACK_BOOL)
					{
						RemoveTrack(track);
					}
				}
			}
		}
	}
	else
	{
		// Saving.
		for (unsigned int i = 0; i < m_tracks.size(); i++)
		{
			IAnimTrack *track = m_tracks[i].track;
			if (track)
			{
				int paramid = m_tracks[i].paramId;
				XmlNodeRef trackNode = xmlNode->newChild( "Track" );
				trackNode->setAttr( "ParamId",m_tracks[i].paramId );
				int nTrackType = track->GetType();
				trackNode->setAttr( "Type",nTrackType );
				track->Serialize( trackNode,bLoading );
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CAnimNode::SetTimeRange( Range timeRange )
{
	for (unsigned int i = 0; i < m_tracks.size(); i++)
	{
		if (m_tracks[i].track)
		{
			m_tracks[i].track->SetTimeRange( timeRange );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
CAnimNode::CAnimNode()
{
	m_id = 0;
	m_pOwner = 0;
	m_pAnimator = 0;
	m_pSequence = 0;
	m_flags = 0;
	m_bIgnoreSetParam = false;
	m_pParentNode = 0;
	m_nLoadedParentNodeId = 0;
}

//////////////////////////////////////////////////////////////////////////
CAnimNode::~CAnimNode()
{
}

//////////////////////////////////////////////////////////////////////////
void CAnimNode::SetFlags( int flags )
{
	m_flags = flags;
}

//////////////////////////////////////////////////////////////////////////
int CAnimNode::GetFlags() const
{
	return m_flags;
}

//////////////////////////////////////////////////////////////////////////
void CAnimNode::Animate( SAnimContext &ec )
{
}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::IsParamValid( int paramId ) const
{
	SParamInfo info;
	if (GetParamInfoFromId(paramId,info))
		return true;
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::SetParamValue( float time,AnimParamType param,float value )
{
	if (m_bIgnoreSetParam)
		return true;

	IAnimTrack *track = GetTrackForParameter(param);
	if (track && track->GetValueType() == AVALUE_FLOAT)
	{
		// Float track.
		bool bDefault = !(gEnv->pMovieSystem->IsRecording() && (m_flags&ANODE_FLAG_SELECTED)); // Only selected nodes can be recorded
		track->SetValue( time,value,bDefault );
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::SetParamValue( float time,AnimParamType param,const Vec3 &value )
{
	if (m_bIgnoreSetParam)
		return true;
	
	CCompoundSplineTrack *track = static_cast<CCompoundSplineTrack*>(GetTrackForParameter(param));
	if (track && track->GetValueType() == AVALUE_VECTOR)
	{
		// Vec3 track.
		bool bDefault = !(gEnv->pMovieSystem->IsRecording() && (m_flags&ANODE_FLAG_SELECTED)); // Only selected nodes can be recorded
		track->SetValue( time,value,bDefault );
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::SetParamValue( float time,AnimParamType param,const Vec4 &value )
{
	if (m_bIgnoreSetParam)
		return true;

	CCompoundSplineTrack *track = static_cast<CCompoundSplineTrack*>(GetTrackForParameter(param));
	if (track && track->GetValueType() == AVALUE_VECTOR4)
	{
		// Vec4 track.
		bool bDefault = !(gEnv->pMovieSystem->IsRecording() && (m_flags&ANODE_FLAG_SELECTED)); // Only selected nodes can be recorded
		track->SetValue( time,value,bDefault );
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::GetParamValue( float time,AnimParamType param,float &value )
{
	IAnimTrack *track = GetTrackForParameter(param);
	if (track && track->GetValueType() == AVALUE_FLOAT && track->GetNumKeys() > 0)
	{
		// Float track.
		track->GetValue( time,value );
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::GetParamValue( float time,AnimParamType param,Vec3 &value )
{
	CCompoundSplineTrack *track = static_cast<CCompoundSplineTrack*>(GetTrackForParameter(param));
	if (track && track->GetValueType() == AVALUE_VECTOR && track->GetNumKeys() > 0)
	{
		// Vec3 track.
		track->GetValue( time,value );
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::GetParamValue( float time,AnimParamType param,Vec4 &value )
{
	CCompoundSplineTrack *track = static_cast<CCompoundSplineTrack*>(GetTrackForParameter(param));
	if (track && track->GetValueType() == AVALUE_VECTOR4 && track->GetNumKeys() > 0)
	{
		// Vec4 track.
		track->GetValue( time,value );
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
void CAnimNode::Serialize( XmlNodeRef &xmlNode,bool bLoading,bool bLoadEmptyTracks )
{
	if (bLoading)
	{
		xmlNode->getAttr( "Id",m_id );
		const char *name = xmlNode->getAttr("Name");
		int flags;
		if (xmlNode->getAttr( "Flags", flags ))
			SetFlags(flags);
		SetName(name);

		m_nLoadedParentNodeId = 0;
		xmlNode->getAttr("ParentNode",m_nLoadedParentNodeId);
	}
	else
	{
		m_nLoadedParentNodeId = 0;
		xmlNode->setAttr( "Id",m_id );
		xmlNode->setAttr("Type", GetType() );
		xmlNode->setAttr("Name", GetName() );
		xmlNode->setAttr( "Flags", GetFlags() );
		if (m_pParentNode)
			xmlNode->setAttr("ParentNode",m_pParentNode->GetId());
	}

	SerializeAnims(xmlNode, bLoading, bLoadEmptyTracks);
}

//////////////////////////////////////////////////////////////////////////
XmlNodeRef CAnimNode::SaveToColladaInFixedFPS(float fps, const ENodeExportType exportType, const IAnimSequence* seq, const IEntity* ent) const
{
	XmlNodeRef nodeCollada	= gEnv->pSystem->CreateXmlNode("COLLADA");
	nodeCollada->setAttr("xmlns", "http://www.collada.org/2005/11/COLLADASchema");
	nodeCollada->setAttr("version", "1.4.1");
	
		// Some meta data
		XmlNodeRef assetNode = nodeCollada->newChild("asset");
			XmlNodeRef contributorNode = assetNode->newChild("contributor");
				contributorNode->newChild("author")->setContent("JJ");
				contributorNode->newChild("authoring_tool")->setContent("CryENGINE TrackView");
				string sourceData;
				sourceData.Format("%s%s", "file://", gEnv->p3DEngine->GetLevelFilePath(""));
				contributorNode->newChild("source_data")->setContent(sourceData.c_str());

			std::time_t time = std::time(0);
			std::tm dateTime = *std::localtime(&time);
			char timeBuffer[1024];
			std::strftime(timeBuffer, sizeof(timeBuffer) / sizeof(timeBuffer[0]), "%Y-%m-%dT%H:%M:%SZ", &dateTime);
			assetNode->newChild("created")->setContent(timeBuffer);
			assetNode->newChild("modified")->setContent(timeBuffer);
			assetNode->newChild("revision")->setContent("1.4.1");
			XmlNodeRef unitNode = assetNode->newChild("unit");
			unitNode->setAttr("meter", 0.01);
			unitNode->setAttr("name", "meter");
			assetNode->newChild("up_axis")->setContent("Z_UP");
			
		// Save each track.
		XmlNodeRef libraryAnimationsNode = nodeCollada->newChild("library_animations");
		for(unsigned int i = 0; i < m_tracks.size(); i++)
		{
			IAnimTrack *track = m_tracks[i].track;
			if(track)
			{
				if(track->GetParameterType() != APARAM_POS 
				&& track->GetParameterType() != APARAM_ROT)
					continue;
				string trackName;
				trackName.Format("%s-%s", m_name.c_str(), GetParamName(track->GetParameterType()));
				track->SaveToColladaInFixedFPS(libraryAnimationsNode, m_name, 
																			trackName.c_str(), fps, exportType, seq, ent);
			}
		}

	return nodeCollada;
}

//////////////////////////////////////////////////////////////////////////
bool CAnimNode::LoadFromCollada(XmlNodeRef xmlNode)
{
	// Some sanity checks
	if (strcmp(xmlNode->getTag(), "COLLADA") != 0)
		return false;
	XmlNodeRef assetNode = xmlNode->findChild("asset");
	if (!assetNode)
		return false;
	XmlNodeRef upAxisNode = assetNode->findChild("up_axis");
	if (!upAxisNode)
		return false;
//	if (strcmp(upAxisNode->getContent(), "Z_UP") != 0)
//		return false;

	return true;
}

//////////////////////////////////////////////////////////////////////////
void CAnimNode::PostLoad()
{
	if (m_nLoadedParentNodeId)
	{
		IAnimNode *pParentNode = ((CAnimSequence*)m_pSequence)->FindNodeById(m_nLoadedParentNodeId);
		m_pParentNode = pParentNode;
		m_nLoadedParentNodeId = 0;
	}
}

//////////////////////////////////////////////////////////////////////////
Matrix34 CAnimNode::GetReferenceMatrix() const
{
	static Matrix34 tm(IDENTITY);
	return tm;
}

IAnimTrack * CAnimNode::CreateTrackInternalFloat( int trackType ) const
{
	IAnimTrack *pTrack;
	switch (trackType)
	{
	case ATRACK_BEZIER_FLOAT:
		pTrack = new C2DSplineTrack;
		break;
	case ATRACK_TCB_FLOAT:
		pTrack = new CTcbFloatTrack;
		break;
	default:
		pTrack = new C2DSplineTrack;
	}	
	return pTrack;
}

 IAnimTrack * CAnimNode::CreateTrackInternalVector( int trackType, const SParamInfo &info ) const
{
	IAnimTrack *pTrack;
	switch (trackType)
	{
	
	case ATRACK_POSITION_XYZ:
	case ATRACK_EULER_XYZ:
	default:
		if(trackType == DEFAULT_TRACK_TYPE )
		{
			if(info.paramId == APARAM_POS || info.paramId == APARAM_SCL)
				trackType = ATRACK_POSITION_XYZ;
			else if(info.paramId == APARAM_ROT)
				trackType = ATRACK_EULER_XYZ;
			else
				trackType = ATRACK_BEZIER_FLOAT;
		}
		{
			AnimParamType subTrackParamTypes[MAX_SUBTRACKS];
			if(info.paramId == APARAM_POS)
			{
				subTrackParamTypes[0] = APARAM_POS_X;
				subTrackParamTypes[1] = APARAM_POS_Y;
				subTrackParamTypes[2] = APARAM_POS_Z;
			}
			else if(info.paramId == APARAM_SCL)
			{
				subTrackParamTypes[0] = APARAM_SCL_X;
				subTrackParamTypes[1] = APARAM_SCL_Y;
				subTrackParamTypes[2] = APARAM_SCL_Z;
			}
			else if(info.paramId == APARAM_ROT)
			{
				subTrackParamTypes[0] = APARAM_ROT_X;
				subTrackParamTypes[1] = APARAM_ROT_Y;
				subTrackParamTypes[2] = APARAM_ROT_Z;
			}
			else if(info.paramId == APARAM_DEPTH_OF_FIELD)
			{
				subTrackParamTypes[0] = APARAM_FOCUS_DISTANCE;
				subTrackParamTypes[1] = APARAM_FOCUS_RANGE;
				subTrackParamTypes[2] = APARAM_BLUR_AMOUNT;
				pTrack = new CCompoundSplineTrack(3, ATRACK_DEPTH_OF_FIELD, (EAnimValue)info.valueType, subTrackParamTypes);
				pTrack->SetSubTrackName(0, "FocusDist");
				pTrack->SetSubTrackName(1, "FocusRange");
				pTrack->SetSubTrackName(2, "BlurAmount");
				return pTrack;
			}		
			else if(info.paramId == APARAM_LIGHT_DIFFUSECOLOR)
			{
				subTrackParamTypes[0] = APARAM_LIGHT_DIFFUSECOLOR_R;
				subTrackParamTypes[1] = APARAM_LIGHT_DIFFUSECOLOR_G;
				subTrackParamTypes[2] = APARAM_LIGHT_DIFFUSECOLOR_B;
				pTrack = new CCompoundSplineTrack(3,(EAnimTrackType)ATRACK_COLOR_RGB,(EAnimValue)info.valueType, subTrackParamTypes);
				pTrack->SetSubTrackName(0, "Red");
				pTrack->SetSubTrackName(1, "Green");
				pTrack->SetSubTrackName(2, "Blue");
				return pTrack;
			}
			else
				assert(0);
			pTrack = new CCompoundSplineTrack(3,(EAnimTrackType)trackType,(EAnimValue)info.valueType, subTrackParamTypes);
		}
		break;
	case ATRACK_TCB_VECTOR:
		pTrack = new CTcbVectorTrack;
		break;
	case -2:
		// Backward compatibility to the CryEngine2
		pTrack = new CTcbVectorTrack;
		break;
	}
	return pTrack;
 }

 IAnimTrack * CAnimNode::CreateTrackInternalQuat( int trackType, const SParamInfo &info ) const
 {
	IAnimTrack *pTrack;
	 switch (trackType)
	 {
	 case ATRACK_EULER_XYZ:
	 default:
		 if(trackType == DEFAULT_TRACK_TYPE )
			{
				if(info.paramId == APARAM_ROT)
					trackType = ATRACK_EULER_XYZ;
			}
			{
				AnimParamType subTrackParamTypes[MAX_SUBTRACKS];
				if(info.paramId == APARAM_ROT)
				{
					subTrackParamTypes[0] = APARAM_ROT_X;
					subTrackParamTypes[1] = APARAM_ROT_Y;
					subTrackParamTypes[2] = APARAM_ROT_Z;
				}
				else
					assert(0);

				pTrack = new CCompoundSplineTrack(3,(EAnimTrackType)trackType,(EAnimValue)info.valueType, subTrackParamTypes);
			}
			break;
	 case ATRACK_TCB_QUAT:
		 pTrack = new CTcbQuatTrack;
		 break;
	 case -2:
		 // Backward compatibility to the CryEngine2
		 pTrack = new CTcbQuatTrack;
	 }
	return pTrack;
 }

 IAnimTrack * CAnimNode::CreateTrackInternalVector4( const SParamInfo &info ) const
 {
	IAnimTrack *pTrack;

	 AnimParamType subTrackParamTypes[MAX_SUBTRACKS];
	 subTrackParamTypes[0] = APARAM_SHAKEMULT_AMPA;
	 subTrackParamTypes[1] = APARAM_SHAKEMULT_AMPB;
	 subTrackParamTypes[2] = APARAM_SHAKEMULT_FREQA;
	 subTrackParamTypes[3] = APARAM_SHAKEMULT_FREQB;
	 if(info.paramId == APARAM_NOISE)
	 {
		 pTrack = new CCompoundSplineTrack(4,ATRACK_NOISE,(EAnimValue)info.valueType, subTrackParamTypes);
		 pTrack->SetSubTrackName(0, "Pos Noise Amp");
		 pTrack->SetSubTrackName(1, "Pos Noise Freq");
		 pTrack->SetSubTrackName(2, "Rot Noise Amp");
		 pTrack->SetSubTrackName(3, "Rot Noise Freq");
	 }
	 else // if(info.paramId == APARAM_SHAKEMULT)
	 {
		 pTrack = new CCompoundSplineTrack(4,ATRACK_CAMERASHAKE,(EAnimValue)info.valueType, subTrackParamTypes);
		 pTrack->SetSubTrackName(0, "Amplitude A");
		 pTrack->SetSubTrackName(1, "Amplitude B");
		 pTrack->SetSubTrackName(2, "Frequency A");
		 pTrack->SetSubTrackName(3, "Frequency B");
	 }

	 return pTrack;
 }

 //////////////////////////////////////////////////////////////////////////
 void CAnimNode::PrecacheData(float startTime) const
 {
 }

 void CAnimNode::PrecacheSoundData() const
 {
	 size_t const nParamCount = m_tracks.size();
	 for (size_t nParamIndex = 0; nParamIndex < nParamCount; ++nParamIndex)
	 {
		 int const nTrackType = m_tracks[nParamIndex].paramId;
		 IAnimTrack const* const pTrack = m_tracks[nParamIndex].track;

		 if( nTrackType == APARAM_SOUND )
		 {
			 // Pre-cache audio data from all tracks
			 CSoundTrack const* const pSoundTrack = static_cast<CSoundTrack const* const>(pTrack);
			 ISoundKey oKey;

			 int const nKeysCount = pTrack->GetNumKeys();
			 for (int nKeyIndex = 0; nKeyIndex < nKeysCount; ++nKeyIndex)
			 {
				 pSoundTrack->GetKey(nKeyIndex, &oKey);
				 gEnv->pSoundSystem->Precache(oKey.pszFilename, 0, oKey.bVoice ? FLAG_SOUND_PRECACHE_DIALOG_DEFAULT : FLAG_SOUND_PRECACHE_EVENT_DEFAULT);
				 //gEnv->pSoundSystem->CreateSound(oKey.pszFilename, FLAG_SOUND_LOAD_SYNCHRONOUSLY | FLAG_SOUND_MOVIE );	
			 }
		 }
	 }
 }


 //////////////////////////////////////////////////////////////////////////
 IAnimNode * CAnimNode::HasDirectorAsParent() const
 {
	 IAnimNode *pParent = GetParent();
	 while(pParent)
	 {
		 if(pParent->GetType() == ANODE_SCENE)
			 return pParent;
		 // There are some invalid data.
		 if(pParent->GetParent() == pParent)
		 {
			 pParent->SetParent(NULL);
			 return NULL;
		 }
		 pParent = pParent->GetParent();
	 }
	 return NULL;
 }

